マウスをぷるぷる動かしてスクリーンセーバを抑制するアプリ

#スクリーンセーバの動作を抑止したい
会社のスクリーンセーバの設定時間が短くなり、電話応対時などにいちいち解除するのがうっとうしかったので、マウスを自動的にぷるぷる動かしてスクリーンセーバの動作を抑止するアプリを作ります。言語はC#で、タスクトレイ常駐型にします。

#作り方
##タスクトレイ常駐型にする

###フォームが起動しないように、 Application.Runメソッドの引数にFormを渡さないようにする。

```lang:C#
using System;
using System.Windows.Forms;

namespace ScreensaverDeter
{
    static class ScreensaverDeter
    {
        /// <summary>
        /// アプリケーションのメイン エントリ ポイントです。
        /// </summary>
        [STAThread]
        static void Main()
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);

            // フォームを表示しない
            new ScreensaverDeterForm();
            Application.Run();
        }
    }
}
```

###Formに「NotifiIcon」を追加して、Iconプロパティに適当な画像を設定する
![WS000004.JPG](https://qiita-image-store.s3.amazonaws.com/0/83813/c0cadb86-966c-37b0-879c-e55b4b28a1b4.jpeg)


##ホットキーで開始／停止を制御する
「CTRL + SHIT + S」で開始、「CTRL + SHIT + D」で停止する。

###user32.dllを読み込んで、ホットキーの登録、解除用メソッドを設定する

```lang:C#
using System;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Runtime.InteropServices;

namespace ScreensaverDeter
{
    public partial class ScreensaverDeterForm : Form
    {
        // ホットキー関連の定数
        // モディファイアキー
        const int MOD_ALT = 0x0001;
        const int MOD_CONTROL = 0x0002;
        const int MOD_SHIFT = 0x0004;
        const int MOD_WIN = 0x0008;

        // ホットキーのイベントを示すメッセージID
        const int WM_HOTKEY = 0x0312;

        // ホットキー登録の際に指定するID
        // 0x0000〜0xbfff 内の適当な値を指定
        const int APP_START_HOTKEY_ID = 0x0001;
        const int APP_STOP_HOTKEY_ID = 0x0002;

        // ホットキーの登録
        [DllImport("user32.dll")]
        extern static int RegisterHotKey(IntPtr HWnd, int ID, int MOD_KEY, int KEY);

        // ホットキーの解除
        [DllImport("user32.dll")]
        extern static int UnregisterHotKey(IntPtr HWnd, int ID);
    }
}
```

###コンストラクタで実際に登録する。他のアプリ等で既に登録済みの場合はアプリを終了する。
```lang:C#
        // 初期化処理
        public ScreensaverDeterForm()
        {
            InitializeComponent();

            // ホットキーの登録
            // CTRL + SHIFT + S　で機能開始
            if (RegisterHotKey(this.Handle, APP_START_HOTKEY_ID, MOD_CONTROL | MOD_SHIFT, (int)Keys.S) == 0)
            {
                MessageBox.Show("「CTRL + SHIFT + S」をホットキーに登録できませんでした。");
                appExit();
            }
            // CTRL + SHIFT + D　で機能停止
            if (RegisterHotKey(this.Handle, APP_STOP_HOTKEY_ID, MOD_CONTROL | MOD_SHIFT, (int)Keys.D) == 0)
            {
                MessageBox.Show("「CTRL + SHIFT + D」をホットキーに登録できませんでした。");
                appExit();
            }
        }
```

###WndProcメソッドをオーバーライドして、対応するホットキーのメッセージを受け取ったら開始、停止用のメソッドを実行する

```lang:C#
        // OSから渡されるメッセージ処理をオーバーライド
        protected override void WndProc(ref Message m)
        {
            base.WndProc(ref m);

            if (m.Msg == WM_HOTKEY)
            {
                if ((int)m.WParam == APP_START_HOTKEY_ID)
                {
                    // 機能開始
                    autoMouseRunStart();
                }
                else if ((int)m.WParam == APP_STOP_HOTKEY_ID)
                {
                    // 機能停止
                    autoMouseRunStop();
                }
            }
        }
```

##マウスを自動的に動かす
現在のマウスポインタの位置を始点にして、左右にぷるぷる動かす。
Cursor.Positionだとスクリーンセーバを抑制できなかったので、SendInputを利用する。

###SendInputメソッドでマウスを制御するための設定をする

```lang:C#
        // マウスイベント関連の定数
        const int MOUSEEVENTF_MOVED = 0x0001;
        const int MOUSEEVENTF_ABSOLUTE = 0x8000;
        const int screen_length = 0x10000;

        // マウス操作を制御するためのMOUSEINPUT構造体
        [StructLayout(LayoutKind.Sequential)]
        struct MOUSEINPUT
        {
            public int dx;
            public int dy;
            public int mouseData;
            public int dwFlags;
            public int time;
            public IntPtr dwExtraInfo;
        }
　　　　 // SendInputメソッド用の構造体
        [StructLayout(LayoutKind.Sequential)]
        struct INPUT
        {
            public int type; 
            public MOUSEINPUT mi;
        }
　　　　 // SendInputメソッドを宣言
        [DllImport("user32.dll")]
        extern static uint SendInput(
            uint nInputs,     // INPUT 構造体の数(イベント数)
            INPUT[] pInputs,  // INPUT 構造体
            int cbSize        // INPUT 構造体のサイズ
        );
```


###機能開始、停止用のメソッドを実装する。停止用のホットキーメッセージを受信できるように、開始用メソッドは非同期にする。

```lang:C#
        // 機能の開始／停止を制御するフラグ
        Boolean isAppRun= false;

        // 機能開始
        private async void autoMouseRunStart()
        {
            isAppRun= true;

            // マウスポインタの位置を取得する
            // X座標を取得する
            int x = System.Windows.Forms.Cursor.Position.X * (65535 / Screen.PrimaryScreen.Bounds.Width);
            // Y座標を取得する
            int y = System.Windows.Forms.Cursor.Position.Y * (65535 / Screen.PrimaryScreen.Bounds.Height);

            // マウス操作イベントの作成
            INPUT[] input = new INPUT[2];
            input[0].mi.dx = x;
            input[0].mi.dy = y;
            input[0].mi.dwFlags = MOUSEEVENTF_MOVED | MOUSEEVENTF_ABSOLUTE;

            input[1].mi.dx = x + 80;
            input[1].mi.dy = y;
            input[1].mi.dwFlags = MOUSEEVENTF_MOVED | MOUSEEVENTF_ABSOLUTE;

            await Task.Run(() =>
            {
                while (isAppRun)
                {
                    // イベントの実行
                    SendInput(2, input, Marshal.SizeOf(input[0]));
                }
            });
        }

        // 機能停止
        private void autoMouseRunStop()
        {
            isAppRun = false;
        }
```
##アプリケーションの終了メニューを追加する
タスクトレイのアイコンを右クリックして、アプリを終了する。

###Formに「contextMenuStrip」を追加して終了メニューを作る
![WS000000.JPG](https://qiita-image-store.s3.amazonaws.com/0/83813/3aa94222-cf32-d156-7efa-195633a8fa51.jpeg)

###「NotifyIcon」のContextMenuStripプロパティに、上記で作成したメニューを設定する。
![WS000005.JPG](https://qiita-image-store.s3.amazonaws.com/0/83813/a6d013ea-d4b7-7ee6-2230-36dbdef74479.jpeg)

###終了用のイベントを実装する。ホットキーを解除して、アイコンを非表示にして、終了する。

```lang:C#
        // 終了メニュー押下
        private void AppExitToolStripMenuItem_Click(object sender, EventArgs e)
        {
            appExit();
        }

        // アプリケーションの終了
        private void appExit()
        {
            // ホットキーの解除
            UnregisterHotKey(this.Handle, APP_START_HOTKEY_ID);
            UnregisterHotKey(this.Handle, APP_STOP_HOTKEY_ID);

            // アプリ終了
            notifyIcon.Visible = false;
            Application.Exit();
        }
```

##完成品
https://github.com/ms-ogawara/ScreensaverDeter