C#で画面にドットサイトをつける

# 目的
画面中央に点を表示させます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405484/e3b1b128-c5a2-f229-3938-2e3ce1edff10.png)
こんな感じです。

必要なこと

* 最前面指定
* フォーム非表示
* 画像表示
* 画面中央配置

以上の4つです。

# 最前面指定
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405484/92a8b355-72c5-0b13-491e-d66790f6c48f.png)
TOPMOST を　TRUE　にする

# フォーム非表示

```C#
private void Form1_Shown(object sender, EventArgs e)
{
    Visible = false;
}
```
このイベントををformクラスに入れる

http://kyasu.sakura.ne.jp/?type=single&id=20141204235901

フォームを非表示で子要素を表示することはできそうにない・・・

# フォームを透明化＋上のバーをなくす

https://johobase.com/windows-form-transparencykey/

https://www.kyoukasho.net/entry/c-sharp-form

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405484/eed2ed16-715b-cc0c-d63e-9a0f63e86512.png)

FormBorderSytleをNoneにすることで、上のバーを無くし
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405484/c8c8e51f-b2ca-c5df-4dda-e0d3c924a947.png)
こんな感じにする

```C#
this.TransparencyKey = this.BackColor;
```
でフォームを透明化する。

# 画像表示

https://dobon.net/vb/dotnet/graphics/pictureboximage.html

PictureBoxを使用

# ディスプレイのサイズを取得
https://dobon.net/vb/dotnet/system/displaysize.html

## 画像がディスプレイの中心に来るようフォームの位置を調整する。

完了
完成プログラム参照


# 非アクティブ時にキー入力で終了させる

https://resanaplaza.com/2022/12/31/%E3%80%90%E3%82%B3%E3%83%94%E3%83%9A%E3%81%A7%E4%BD%BF%E3%81%88%E3%82%8B%E3%80%91c%E3%81%8B%E3%82%89key-hook%E3%81%A7%E3%82%AD%E3%83%BC%E5%85%A5%E5%8A%9B%E3%82%92%E5%8F%96%E5%BE%97%E3%81%99%E3%82%8B/

WindowsAPIの機能を使う

# 完成したプログラム
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405484/0f8a8572-4cfb-78d2-5202-dd9e0405d36f.png)

フォルダ内に表示する画像を入れてください。
画像の名前に合わせてプログラムを修正してください。
デフォルトでpキーでソフト終了にしてます。

```C#
using System.Diagnostics;
using System.Runtime.InteropServices;

namespace DotSight
{
    public partial class Form1 : Form
    { 

        //キーボードフッククラスのインスタンス生成
        KeyboardHook _hook = new KeyboardHook();
    
        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            //ディスプレイの高さ
            int h = System.Windows.Forms.Screen.PrimaryScreen.Bounds.Height;
            //ディスプレイの幅
            int w = System.Windows.Forms.Screen.PrimaryScreen.Bounds.Width;

            //結果表示
            //label1.Text = "ディスプレイの高さ:"+ h.ToString() +"ピクセル";
            //label2.Text = "ディスプレイの幅:" + w.ToString() + "ピクセル";

            Image image = Image.FromFile(@"C:/Users/yellow_site.png");

            pictureBox1.Image = image;

            //画像のサイズを取得する
            int h_png = image.Height;
            int w_png = image.Width;

            //フォームの場所を調整する
            this.Location = new Point((int)((w-w_png)/2),(int)((h-h_png)/2));

            //キーボードが押された時のイベントハンドラを登録 
            _hook.OnKeyDown += ( s , ea) =>
            {
                //イベント発生時の処理
                //Console.WriteLine(ea.Key);

                ea.RetCode = 0; //他のプログラムにキーを転送したくない場合は1を代入,それ以外は0

                
                if (ea.Key.ToString() == "P")
                {
                    //アプリ終了
                    _hook.UnHook();
                    this.Close();
                }

            };
            //キーボードフックの開始
            _hook.Hook();


        }

        private void label1_Click(object sender, EventArgs e)
        {

        }

        private void Form1_Shown(object sender, EventArgs e)
        {
            //this.Visible = false;
            //pictureBox1.Visible = true;
            this.TransparencyKey = this.BackColor;
        }

        private void pictureBox1_Click(object sender, EventArgs e)
        {

        }
    }

    public class KeyboardHook
    {
        private const int WH_KEYBOARD_LL = 0x0D;
        private const int WM_KEYBOARD_DOWN = 0x100;
        private const int WM_KEYBOARD_UP = 0x101;
        private const int WM_SYSKEY_DOWN = 0x104;
        private const int WM_SYSKEY_UP = 0x105;

        //イベントハンドラの定義
        public event EventHandler<KeyboardHookEventArgs> OnKeyDown = delegate { };
        public event EventHandler<KeyboardHookEventArgs> OnKeyUp = delegate { };

        //コールバック関数のdelegate 定義
        private delegate IntPtr HookCallback(int nCode, IntPtr wParam, IntPtr lParam);

        //キーボードフックに必要なDLLのインポート
        [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        private static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam, IntPtr lParam);

        [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        private static extern IntPtr SetWindowsHookEx(int idHook, HookCallback lpfn, IntPtr hMod, uint dwThreadId);

        [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        private static extern bool UnhookWindowsHookEx(IntPtr hhk);

        [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        private static extern IntPtr GetModuleHandle(string lpModuleName);

        //フックプロシージャのハンドル
        private static IntPtr _hookHandle = IntPtr.Zero;

        //フック時のコールバック関数
        private static HookCallback _callback;

        /// <summary>
        /// キーボードHook の開始
        /// </summary>
        /// <param name="callback"></param>
        public void Hook()
        {
            _callback = CallbackProc;
            using (Process process = Process.GetCurrentProcess())
            {
                using (ProcessModule module = process.MainModule)
                {
                    _hookHandle = SetWindowsHookEx(
                       WH_KEYBOARD_LL,                                          //フックするイベントの種類
                       _callback, //フック時のコールバック関数
                       GetModuleHandle(module.ModuleName),                      //インスタンスハンドル
                       0                                                        //スレッドID（0：全てのスレッドでフック）
                   );
                }
            }
        }
        /// <summary>
        /// コールバック関数
        /// </summary>
        /// <param name="nCode"></param>
        /// <param name="wParam"></param>
        /// <param name="lParam"></param>
        /// <returns></returns>
        private IntPtr CallbackProc(int nCode, IntPtr wParam, IntPtr lParam)
        {
            var args = new KeyboardHookEventArgs();
            Keys key = (Keys)(short)Marshal.ReadInt32(lParam);
            args.Key = key;

            if ((int)wParam == WM_KEYBOARD_DOWN || (int)wParam == WM_SYSKEY_DOWN) OnKeyDown(this, args);
            if ((int)wParam == WM_KEYBOARD_UP || (int)wParam == WM_SYSKEY_UP) OnKeyUp(this, args);

            return (args.RetCode == 0) ? CallNextHookEx(_hookHandle, nCode, wParam, lParam) : (IntPtr)1;
        }
        /// <summary>
        /// キーボードHockの終了
        /// </summary>
        public void UnHook()
        {
            UnhookWindowsHookEx(_hookHandle);
            _hookHandle = IntPtr.Zero;
        }
    }

    /// <summary>
    /// キーボードフックのイベント引数
    /// </summary>
    public class KeyboardHookEventArgs
    {
        public Keys Key { get; set; }
        public int RetCode { get; set; } = 0;
    }
}
```

フォームデザイン

```C#
namespace DotSight
{
    partial class Form1
    {
        /// <summary>
        ///  Required designer variable.
        /// </summary>
        private System.ComponentModel.IContainer components = null;

        /// <summary>
        ///  Clean up any resources being used.
        /// </summary>
        /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
        protected override void Dispose(bool disposing)
        {
            if (disposing && (components != null))
            {
                components.Dispose();
            }
            base.Dispose(disposing);
        }

        #region Windows Form Designer generated code

        /// <summary>
        ///  Required method for Designer support - do not modify
        ///  the contents of this method with the code editor.
        /// </summary>
        private void InitializeComponent()
        {
            this.pictureBox1 = new System.Windows.Forms.PictureBox();
            ((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).BeginInit();
            this.SuspendLayout();
            // 
            // pictureBox1
            // 
            this.pictureBox1.Location = new System.Drawing.Point(0, 0);
            this.pictureBox1.Name = "pictureBox1";
            this.pictureBox1.Size = new System.Drawing.Size(130, 113);
            this.pictureBox1.TabIndex = 2;
            this.pictureBox1.TabStop = false;
            this.pictureBox1.Click += new System.EventHandler(this.pictureBox1_Click);
            // 
            // Form1
            // 
            this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 15F);
            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
            this.ClientSize = new System.Drawing.Size(214, 250);
            this.Controls.Add(this.pictureBox1);
            this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None;
            this.Name = "Form1";
            this.Text = "Form1";
            this.TopMost = true;
            this.Load += new System.EventHandler(this.Form1_Load);
            this.Shown += new System.EventHandler(this.Form1_Shown);
            ((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).EndInit();
            this.ResumeLayout(false);

        }

        #endregion
        private PictureBox pictureBox1;
    }
}
```

## 追記

キー入力イベントクラス

https://resanaplaza.com/2022/12/31/%E3%80%90%E3%82%B3%E3%83%94%E3%83%9A%E3%81%A7%E4%BD%BF%E3%81%88%E3%82%8B%E3%80%91c%E3%81%8B%E3%82%89key-hook%E3%81%A7%E3%82%AD%E3%83%BC%E5%85%A5%E5%8A%9B%E3%82%92%E5%8F%96%E5%BE%97%E3%81%99%E3%82%8B/

作成者様ありがとうございます。

すぐ使えるように、自分用にメモを残します。

```C#:form_load内部
//キーボードが押された時のイベントハンドラを登録 
_hook.OnKeyDown += (s, ea) =>
{
    //イベント発生時の処理
    //Console.WriteLine(ea.Key);

    ea.RetCode = 0; //他のプログラムにキーを転送したくない場合は1を代入,それ以外は0

    textBox1.Text = ea.Key.ToString();

    if (ea.Key.ToString() == "P")
    {
        //アプリ終了
        _hook.UnHook();
        this.Close();
    }

    if (ea.Key.ToString() == "Right")
    {
        screen_shot();
    }

};
//キーボードフックの開始
_hook.Hook();

```

```C#:クラス、namespaceに追加
public class KeyboardHook
    {
        private const int WH_KEYBOARD_LL = 0x0D;
        private const int WM_KEYBOARD_DOWN = 0x100;
        private const int WM_KEYBOARD_UP = 0x101;
        private const int WM_SYSKEY_DOWN = 0x104;
        private const int WM_SYSKEY_UP = 0x105;

        //イベントハンドラの定義
        public event EventHandler<KeyboardHookEventArgs> OnKeyDown = delegate { };
        public event EventHandler<KeyboardHookEventArgs> OnKeyUp = delegate { };

        //コールバック関数のdelegate 定義
        private delegate IntPtr HookCallback(int nCode, IntPtr wParam, IntPtr lParam);

        //キーボードフックに必要なDLLのインポート
        [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        private static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam, IntPtr lParam);

        [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        private static extern IntPtr SetWindowsHookEx(int idHook, HookCallback lpfn, IntPtr hMod, uint dwThreadId);

        [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        private static extern bool UnhookWindowsHookEx(IntPtr hhk);

        [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        private static extern IntPtr GetModuleHandle(string lpModuleName);

        //フックプロシージャのハンドル
        private static IntPtr _hookHandle = IntPtr.Zero;

        //フック時のコールバック関数
        private static HookCallback _callback;

        /// <summary>
        /// キーボードHook の開始
        /// </summary>
        /// <param name="callback"></param>
        public void Hook()
        {
            _callback = CallbackProc;
            using (Process process = Process.GetCurrentProcess())
            {
                using (ProcessModule module = process.MainModule)
                {
                    _hookHandle = SetWindowsHookEx(
                       WH_KEYBOARD_LL,                                          //フックするイベントの種類
                       _callback, //フック時のコールバック関数
                       GetModuleHandle(module.ModuleName),                      //インスタンスハンドル
                       0                                                        //スレッドID（0：全てのスレッドでフック）
                   );
                }
            }
        }
        /// <summary>
        /// コールバック関数
        /// </summary>
        /// <param name="nCode"></param>
        /// <param name="wParam"></param>
        /// <param name="lParam"></param>
        /// <returns></returns>
        private IntPtr CallbackProc(int nCode, IntPtr wParam, IntPtr lParam)
        {
            var args = new KeyboardHookEventArgs();
            Keys key = (Keys)(short)Marshal.ReadInt32(lParam);
            args.Key = key;

            if ((int)wParam == WM_KEYBOARD_DOWN || (int)wParam == WM_SYSKEY_DOWN) OnKeyDown(this, args);
            if ((int)wParam == WM_KEYBOARD_UP || (int)wParam == WM_SYSKEY_UP) OnKeyUp(this, args);

            return (args.RetCode == 0) ? CallNextHookEx(_hookHandle, nCode, wParam, lParam) : (IntPtr)1;
        }
        /// <summary>
        /// キーボードHockの終了
        /// </summary>
        public void UnHook()
        {
            UnhookWindowsHookEx(_hookHandle);
            _hookHandle = IntPtr.Zero;
        }
    }

    /// <summary>
    /// キーボードフックのイベント引数
    /// </summary>
    public class KeyboardHookEventArgs
    {
        public Keys Key { get; set; }
        public int RetCode { get; set; } = 0;
    }
```