20210315のC#に関する記事は8件です。

LINQPadに複数テキスト行入力ダイアログを追加

C#erのみなさんは Linqpad を使っている方も多いかと思います。
ちょっとしたデータ分析では結果を表やグラフで出力でき、nugetも使えてデバッガも動きVisualStudioよりも便利なことも多いです。

そんな利用頻度の高いLinqpadですが、データとしてcsvやjsonなどの複数行テキストを入力したい場合、一度ファイルに保存して、そのファイル名を入力する形となるのが若干億劫です。1行だけなら以下のようにLinqpad標準の機能はあるのですが。

var filename = Util.Readline("ファイル名を入力してください");

使い捨てのデータをわざわざファイル作成するのも面倒なので、実行中にテキストをペーストできるよう複数行入力ダイアログ機能を入れましょう。

利用イメージ

使い方は画像内のコードでイメージできるかと思いますが MyExtensions.ReadLines() で読み込みます。メソッド戻り値はstring[]、キャンセルされた場合は空配列(string[0])が戻ります。
image.png

ソース

以下コードをMy Queries/My Extensionsの中にあるMy Extensions クラス内にコピペし保存すれば どのQueryでも上記の画像のように使えるようになります。

public static string[] ReadLines(string title = null)
{
    var win = new System.Windows.Window() { Width = 400, Height = 300, Title = title ?? string.Empty };
    var grid = new System.Windows.Controls.Grid();
    grid.RowDefinitions.Add(new System.Windows.Controls.RowDefinition() { Height = new System.Windows.GridLength(1, System.Windows.GridUnitType.Star) });
    grid.RowDefinitions.Add(new System.Windows.Controls.RowDefinition() { Height = new System.Windows.GridLength(36) });
    var margin3 = new System.Windows.Thickness(3);
    var tbox = new System.Windows.Controls.TextBox()
    {
        Margin = margin3,
        AcceptsReturn = true,
        AcceptsTab = true,
        VerticalScrollBarVisibility = System.Windows.Controls.ScrollBarVisibility.Auto,
        HorizontalScrollBarVisibility = System.Windows.Controls.ScrollBarVisibility.Auto,
    };
    var btnOk = new System.Windows.Controls.Button() { Content = "OK", Margin = margin3, Width = 80 };
    var btnCancel = new System.Windows.Controls.Button() { Content = "Cancel", Margin = margin3, Width = 80 };
    btnOk.Click += (_, _) => { win.DialogResult = true; win.Close(); };
    btnCancel.Click += (_, _) => { win.DialogResult = false; win.Close(); };
    var sp = new System.Windows.Controls.StackPanel()
    {
        Orientation = System.Windows.Controls.Orientation.Horizontal,
        HorizontalAlignment = System.Windows.HorizontalAlignment.Right,
    };
    sp.Children.Add(btnOk);
    sp.Children.Add(btnCancel);
    System.Windows.Controls.Grid.SetRow(tbox, 0);
    System.Windows.Controls.Grid.SetRow(sp, 1);
    grid.Children.Add(tbox);
    grid.Children.Add(sp);
    win.Content = grid;
    return win.ShowDialog() == true ? tbox.Text.Split("\r\n") : new string[0];
}
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

サイバーエージェントの3dayハッカソンに参加してきたので所感

初投稿です(CAの方にブログ書くのをお勧めしていただいたのではじめの一歩!)

3/12-3/14の三日間でサイバーエージェントの3dayハッカソンに参加してきました。
技術的な話はしませんが、これからCAの説明会やインターンシップに出ようと思ってる人は是非

今回初めてサーバーサイドの人と連携してゲーム制作をしました。ランキングとユーザー認証の実装を必須とし、お題はシューティングorパズルで、自分のチームはシューティングを作りました。
(僕はインゲーム部分担当)
初めてサーバーサイドと一緒に開発して思ったことは、互いに互いの言語や、使い方の知識がなく、情報共有が大変でした。
また、自分のチームはガチャの実装もしたので、初めてソシャゲを1から作る体験をしたような実習となりました。ガチャやランキングがあることで意識するゲームデザインなども出てくるので、普段とはかなり違う目線でゲーム開発を出来たと思います。

3日目終了後にはCAのSGE(ゲーム系子会社の協力体制的な)の説明や、社員さんによるLT会を開いていただきました。特に木原さんのお話(エンジニアとして大事なこと)は、個人の本音を聞いてる感じがしてとてもよかったです。
最後にグループに分かれて懇親会をしました。コロナ中の新卒研修のお話や、リモートワークのお話などを聞けて、短い時間でしたがとても楽しかったです。

こんな感じでふわふわした感じで終わってしまいますが、技術的なことを何も載せないのはあれなので最後に社員さんにお勧めされた書籍の紹介を(ダイマ)

Unityの技術書ですが、これからUnityを始める人向けではなく、ある程度やってる人が製品レベルまで完成度を上げる際に参考にする書籍かなと思います。

では

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

アルゴリズム 線形データ構造

配列などで知られる線形データ構造とそれを用いた2つのアルゴリズムを紹介します。

線形データ構造とは

線形データ構造はデータの要素が規則性をもった線形の連なりを成している構造のことです。
最初と最後の要素以外には、一つ前と一つ後の要素がある状態です。
例としては配列、キュー、スタックなどなどがこれに該当します。

配列(Array)

配列はもっとも単純な線形データ構造のひとつです。
n個の同じ型のデータがメモリ上に連続して保持される構造です。
配列の中の要素には指数(index)で扱うことができます。
多くのプログラミング言語においてその指数は0からn-1の間の実数値になっています。
配列の中にあるどの位置の要素であろうと新しい値を代入するのにかかる時間に違いはあまりありません。

連結リスト(Linked List)

連結リストは0あるいはそれ以上の数のノードと呼ばれるデータ要素を保持しています。
ノードにはデータ要素の他に1つあるいは2つのポインタと呼ばれる同連結リスト内の他のノードを指すリンクが保持されています。次あるいは前に繋がるノードが内場合にはnullのポインタが保持されます。
連結リストには

  • 片方向 (single linked list)
  • 双方向 (double linked list)

の2つがあり、片方向の場合は次ノードへのリンクのみで、最後のノードのリンクがnullになります。
双方向の場合は次ノードと前ノードへのリンクがあり、先頭には前ノードのリンクがnullで、最後のノードのリンクがnullになります。
ポインタによって次のノードの位置を定義しておかなければならない、ということは連結リストの各要素はメモリ上に連続して保持されていないということになります。
連結リストは動的なデータ構造で、その長さは要素の数と等しくなります。

配列と連結リストの違い

連結リストと配列のどちらもリストというより抽象的なデータ構造の実装に使えます。リストは順序をもって連なったデータ構造です。リストを実装するうえで、以下の機能が求められます。

  • 要素へのアクセス
  • 要素の検索
  • 要素の追加
  • 要素の削除

しかし連結リストと配列では機能の効率性において違いがあります。

要素へのアクセス

$i$番目のデータにアクセスする場合
配列なら指数$i$をもって参照するだけですが、連結リストの場合は$i-1$に至るまで連結リスト内のノードを辿っていく必要があります。

要素の検索

配列内のとある要素を検索したい場合
配列では一つ一つ参照して比較するか、配列がソートされている場合はのちに解説する二分探索によって探すことができます。
連結リストではノードを順に辿って比較していく必要があります。

要素の追加

ある要素をリストの先頭に追加したい場合
配列では配列内の既存の要素すべてをまず移動させてから、新しい値を先頭においた配列に移動し直さなければならないため、要素の数が多くなってくればくるほど非効率的になっていきます。
連結リストにおいては既存の先頭ノードを次へのリンクにした要素を追加するだけなのでとても効率的です。

要素の削除

ある要素をリストから削除したい場合
配列では削除後の残っている要素すべて移動させる必要があるため、要素数が多くなればなるほど非効率的です。
連結リストにおいては移動の必要がありませんが、ノードを辿って削除するノードの前後のノードのポインタを書き換える必要があります。

スタック(stack)

スタックは要素が連なって保持されているという抽象的なデータ構造ですが、その一番上の値にしか参照できません。
要素を追加する場合は一番上に追加され、削除する場合は一番上が追加されます。だるま落としのように積まれていく感じですね、横から叩けませんが。
スタックはLIFO(後入れ先出し法)のデータ構造と呼ばれるものです。
スタックは配列や連結リストで実装できるデータ構造ですが、一番上以外の値を直接参照することができません。
僕もまだアルゴリズムは勉強中でそんなに例はぱっと出てきませんが、例えばエディタにおけるUndoのシステムや、数式やプログラミング言語におけるインデントや丸括弧、波括弧、引用符などが閉じられているかの確認する関数などで使われていると思います。

キュー(Queue)

キューは要素が連なって保持されているという抽象的なデータ構造で、要素の追加は最後に追加され、削除や参照は先頭から行われるというものです。キューの名の通り、店の前の行列などが例ですね。
キューに値を追加することをenqueue、削除することをdequeueと言います。
キューは配列や連結リストで実装できるデータ構造で、スタック同様、途中の要素に直接参照する事ができないものです。
例としてはCPUのスケジューリングやIOバースト処理などでしょうか。

線形データ構造の検索アルゴリズム

検索アルゴリズムは比較によって成り立っています。
求められる役割としては求められた値が存在するか、あるいは求められた値をキーとした要素を探すかです。
今回は木構造を用いたアルゴリズムを行いません、あくまで線形データ構造の検索アルゴリズムです。
最も有名な検索アルゴリズムには以下の2つがあります。

  • 線形探索 (Linear Search)
  • 二分探索 (Binary Search)

線形探索 (Linear Search)

線形探索は端的に言って総当り方式 (Brute force)になります。
総当りとはデータ構造の端から求められた値が見つかるか、最後に至るまで求められた値との比較を行うアルゴリズムです。
データ構造の要素が順に並んでいない場合は線形探索しか選択肢がありません。
このアルゴリズムにおける最悪のシナリオは求められた値がデータ構造の最後の要素に至るまで見つからない場合です。

擬似コード

ALGORITHM~LinearSearch(A[0..n-1], ~K)\\
i~\leftarrow~0\\
While~i<n~AND~A[i]\neq K~do\\
i = i + 1\\
if~i <n~return~i\\
else~return~-1

分析

このアルゴリズムにおいて最も頻繁に行われる処理は$A[i]\neq K$ですので、これが最も基礎的な処理になりますが、先に$i<n$があるのでこれが真となるときにはこの基礎処理は行われません。
上記したように最悪のシナリオは求められた値がデータ構造の最後の要素、あるいはそもそも存在しない場合ですのでそれは漸近記法ではこうなります。

C_{worst}(n)=n\in O(n)

二分探索 (Binary Search)

二分探索は分割統治法(Decrease and conquer)にあてはまります。問題が大きく困難な場合に小さな規模に分けていくものです。
この探索法はデータ構造がソートされている場合にしか行なえません。
手順としましては、データ構造を真ん中で分けて半分の片方から探していきます。その時点で最初の値で見つかれば探索は終了です。なければもう片方を見るという形です。
半分のうち最初ではない位置の中に求められている要素があった場合、半分の構造をさらに半分にして、その片方から探していきます。この分割を最後の一つになるまで繰り返していきます。

擬似コード

ALGORITHM~BinarySearch(A[0..n-1], K)\\
i\leftarrow 0,~j\leftarrow n-1\\
while~i\leq j~do\\
m\leftarrow (i+j)/2\\
if~K=A[m]~return~m\\
else~if~K<A[m]~j\leftarrow m-1\\
else~i\leftarrow m+1\\
return~-1

分析

二分探索においても最悪のケースは計3つに分かれている処理と2つの条件分岐が最も多く行われる場合です。
その場合は漸近記法では

C_{worst}(n) = \log_2 n + 1 \in O(\log n)

となります。

備考

アルゴリズムの基礎と擬似コード、分析については僕が以前書いたこちら

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

パソコンのスクリーンセーバーをデスクトップアプリで表示させなくする【C#】

動機

前回、マウスロガーを作成しましたが、マウス位置情報のダミーを Windows 側に送ることで、スクリーンセーバーを無効化できることがわかりました。

(マウスのダミー位置情報を提供するだけで、実際のマウス操作には影響ありませんでした。)

そこで、今回はスクリーンセーバを C# アプリで無効化するアプリのプロトタイプを作成しました。

なお、本アプリは WindowsFormsApp で作成しました。

過去の記事(WpfApp)

参考 1:ノートパソコンの画面の明るさをボタン一つで変更する【C#】
参考 2:ノートパソコンの画面の音量をボタン一つで変更する【C#】
参考 3:ノートパソコンの画面のバッテリー状況をボタン一つで取得する【C#】
参考 4:ノートパソコンの画面の通信料状況をボタン一つで取得する【C#】
参考 5:ノートパソコンで現在使用中のアプリをボタン一つで把握する【C#】
参考 6: ノートパソコンで現在使用中のオーディオ機器情報をボタン一つで取得する【C#】
参考 7:パソコンのユーザー情報取得やアプリ起動・終了、ディスプレイ画面の切替などを、ボタンを作って動かす【C#】

過去の記事(WindowsFormsApp)

参考 1:パソコンに接続しているディスプレイ情報をボタン一つで取得する【C#】
参考 2:パソコンに入力されたキーを表示する【C#】
参考 3:パソコンに入力されたマウスの位置を表示する【C#】

イメージ画像

今回の UI は非常にシンプルで、無くても良いぐらいでした。

ただ、UI をなくしてしまうとアプリが起動中か個人的に分からなくなってしまうので、非常に小さい UI を作成しました。

なお、このアプリが起動中は、特にマウス操作・キーボード動作をしなくてもスクリーンセーバーは起動しなくなります。

キャプチャ.PNG

ソース

Form1.Designer.cs

Form1.Designer.cs
namespace WindowsFormsApp1
{
    partial class Form1
    {
        /// <summary>
        /// 必要なデザイナー変数です。
        /// </summary>
        private System.ComponentModel.IContainer components = null;

        /// <summary>
        /// 使用中のリソースをすべてクリーンアップします。
        /// </summary>
        /// <param name="disposing">マネージド リソースを破棄する場合は true を指定し、その他の場合は false を指定します。</param>
        protected override void Dispose(bool disposing)
        {
            if (disposing && (components != null))
            {
                components.Dispose();
            }
            base.Dispose(disposing);
        }

        #region Windows フォーム デザイナーで生成されたコード

        /// <summary>
        /// デザイナー サポートに必要なメソッドです。このメソッドの内容を
        /// コード エディターで変更しないでください。
        /// </summary>
        private void InitializeComponent()
        {
            this.label1 = new System.Windows.Forms.Label();
            this.SuspendLayout();
            //
            // label1
            //
            this.label1.AutoSize = true;
            this.label1.Font = new System.Drawing.Font("MS UI Gothic", 18F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(128)));
            this.label1.Location = new System.Drawing.Point(0, 3);
            this.label1.Name = "label1";
            this.label1.Size = new System.Drawing.Size(280, 30);
            this.label1.TabIndex = 0;
            this.label1.Text = "スクリーンセーバー起動なし";
            this.label1.Click += new System.EventHandler(this.label1_Click);
            //
            // Form1
            //
            this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F);
            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
            this.ClientSize = new System.Drawing.Size(232, 33);
            this.Controls.Add(this.label1);
            this.Name = "Form1";
            this.Text = "Activator";
            this.ResumeLayout(false);
            this.PerformLayout();

        }

        #endregion

        private System.Windows.Forms.Label label1;
    }
}

Form1.cs

Form1.cs
using System;
using System.Runtime.InteropServices;
using System.Windows.Forms;

namespace WindowsFormsApp1
{
    public partial class Form1 : Form
    {
        Timer Timer = new Timer();

        public Form1()
        {
            InitializeComponent();
            Timer.Interval = 30 * 1000;
            Timer.Tick += Timer_Tick;
            Timer.Start();
        }

        private void Timer_Tick(object sender, EventArgs e)
        {
            PreventScreenSaverFromStarting();
        }

        [StructLayout(LayoutKind.Sequential)]
        struct MOUSEINPUT
        {
            public int dx;
            public int dy;
            public uint mouseData;
            public uint dwFlags;
            public uint time;
            public IntPtr dwExtraInfo;
        }

        [StructLayout(LayoutKind.Sequential)]
        struct KEYBDINPUT
        {
            ushort wVk;
            ushort wScan;
            uint dwFlags;
            uint time;
            IntPtr dwExtraInfo;
        }

        [StructLayout(LayoutKind.Sequential)]
        struct HARDWAREINPUT
        {
            uint uMsg;
            ushort wParamL;
            ushort wParamH;
        }

        [StructLayout(LayoutKind.Explicit)]
        struct INPUT
        {
            [FieldOffset(0)]
            public int type;
            [FieldOffset(4)] //*
            public MOUSEINPUT mi;
            [FieldOffset(4)] //*
            public KEYBDINPUT ki;
            [FieldOffset(4)] //*
            public HARDWAREINPUT hi;
        }

        const int INPUT_MOUSE = 0;


        [DllImport("user32.dll", SetLastError = true)]
        static extern uint SendInput(uint nInputs, ref INPUT pInputs, int cbSize);
        void PreventScreenSaverFromStarting()
        {
            INPUT input = new INPUT();
            input.type = INPUT_MOUSE;
            input.mi = new MOUSEINPUT();

            input.mi.dwExtraInfo = IntPtr.Zero;
            input.mi.dx = 0;
            input.mi.dy = 0;
            input.mi.time = 0;
            input.mi.mouseData = 0;
            input.mi.dwFlags = 0x0001; // MOVE (RELATIVE)
            int cbSize = Marshal.SizeOf(typeof(INPUT));
            uint r = SendInput(1, ref input, cbSize);
        }

        private void label1_Click(object sender, EventArgs e)
        {

        }
    }
}

おわりに

個人的にデスクトップアプリ側で、スクリーンセーバーの制御(主にスクリーンセーバーを表示させなくすること)ができればいいなと思っていたのですが、このようにマウスのダミー位置情報を与えれ上げればスクリーンセーバを表示させなくすることができると知れたので、大きな収穫でした。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

パソコンに入力されたマウスの位置を表示する【C#】

動機

前回、Windows でキーロガーをプロトタイプで作成しましたが、キーロガーできたならマウスロガーもできるのでは、と思ったのがきっかけです。

そこで、今回はマウスロガーを作ってみました。マウスの位置が、作成したデスクトップアプリ上に表示されるようになります。

なお、本アプリは WindowsFormsApp で作成しました。

過去の記事(WpfApp)

参考 1:ノートパソコンの画面の明るさをボタン一つで変更する【C#】
参考 2:ノートパソコンの画面の音量をボタン一つで変更する【C#】
参考 3:ノートパソコンの画面のバッテリー状況をボタン一つで取得する【C#】
参考 4:ノートパソコンの画面の通信料状況をボタン一つで取得する【C#】
参考 5:ノートパソコンで現在使用中のアプリをボタン一つで把握する【C#】
参考 6: ノートパソコンで現在使用中のオーディオ機器情報をボタン一つで取得する【C#】
参考 7:パソコンのユーザー情報取得やアプリ起動・終了、ディスプレイ画面の切替などを、ボタンを作って動かす【C#】

過去の記事(WindowsFormsApp)

参考 1:パソコンに接続しているディスプレイ情報をボタン一つで取得する【C#】
参考 2:パソコンに入力されたキーを表示する【C#】

イメージ画像

今回も UI は特にこだわってはいません。

マウスの一座標が表示されるだけの、シンプルな UI となっております。

mouse_logger.gif

ソース

Form1.Designer.cs

Form1.Designer.cs
namespace WindowsFormsApp1
{
    partial class Form1
    {
        /// <summary>
        /// 必要なデザイナー変数です。
        /// </summary>
        private System.ComponentModel.IContainer components = null;

        /// <summary>
        /// 使用中のリソースをすべてクリーンアップします。
        /// </summary>
        /// <param name="disposing">マネージド リソースを破棄する場合は true を指定し、その他の場合は false を指定します。</param>
        protected override void Dispose(bool disposing)
        {
            if (disposing && (components != null))
            {
                components.Dispose();
            }
            base.Dispose(disposing);
        }

        #region Windows フォーム デザイナーで生成されたコード

        /// <summary>
        /// デザイナー サポートに必要なメソッドです。このメソッドの内容を
        /// コード エディターで変更しないでください。
        /// </summary>
        private void InitializeComponent()
        {
            this.label1 = new System.Windows.Forms.Label();
            this.label2 = new System.Windows.Forms.Label();
            this.SuspendLayout();
            //
            // label1
            //
            this.label1.AutoSize = true;
            this.label1.Font = new System.Drawing.Font("MS UI Gothic", 31.8F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(128)));
            this.label1.Location = new System.Drawing.Point(204, 161);
            this.label1.Name = "label1";
            this.label1.Size = new System.Drawing.Size(152, 54);
            this.label1.TabIndex = 0;
            this.label1.Text = "label1";
            //
            // label2
            //
            this.label2.AutoSize = true;
            this.label2.Font = new System.Drawing.Font("MS UI Gothic", 24F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(128)));
            this.label2.Location = new System.Drawing.Point(8, 173);
            this.label2.Name = "label2";
            this.label2.Size = new System.Drawing.Size(208, 40);
            this.label2.TabIndex = 1;
            this.label2.Text = "マウス座標:";
            //
            // Form1
            //
            this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F);
            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
            this.ClientSize = new System.Drawing.Size(800, 450);
            this.Controls.Add(this.label2);
            this.Controls.Add(this.label1);
            this.Name = "Form1";
            this.Text = "Form1";
            this.Load += new System.EventHandler(this.Form1_Load);
            this.ResumeLayout(false);
            this.PerformLayout();

        }

        #endregion

        private System.Windows.Forms.Label label1;
        private System.Windows.Forms.Label label2;
    }
}

Form1.cs

Form1.cs
using MouseHook;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Drawing;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace WindowsFormsApp1
{
    public partial class Form1 : Form
    {
        private IntPtr hHook;
        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            SetHook();
        }
        private int SetHook()
        {
            IntPtr hmodule = WindowsAPI.GetModuleHandle(Process.GetCurrentProcess().MainModule.ModuleName);

            hHook = WindowsAPI.SetWindowsHookEx((int)WindowsAPI.HookType.WH_MOUSE_LL, (WindowsAPI.HOOKPROC)MyHookProc, hmodule, IntPtr.Zero);
            if (hHook == null)
            {
                MessageBox.Show("SetWindowsHookEx 失敗", "Error");
                return -1;
            }
            else
            {
                MessageBox.Show("SetWindowsHookEx 成功", "OK");
                return 0;
            }
        }

        private IntPtr MyHookProc(int nCode, IntPtr wParam, IntPtr lParam)
        {
            if (0 == WindowsAPI.HC_ACTION)
            {
                WindowsAPI.MSLLHOOKSTRUCT MouseHookStruct = (WindowsAPI.MSLLHOOKSTRUCT)Marshal.PtrToStructure(lParam, typeof(WindowsAPI.MSLLHOOKSTRUCT));
                label1.Text = string.Format("Mouse Position : {0:d}, {1:d}", MouseHookStruct.pt.x, MouseHookStruct.pt.y);
            }

            return WindowsAPI.CallNextHookEx(hHook, nCode, wParam, lParam);

        }

        private void FormMain_FormClosing(object sender, FormClosingEventArgs e)
        {
            WindowsAPI.UnhookWindowsHookEx(hHook);
        }
    }
}

おわりに

今回はマウスの位置座標を取得するデスクトップを作成しました。

誰かのお役に立てればと思います。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

パソコンに入力されたキーを表示する【C#】

動機

今回はボタン一つで何か処理をする・表示するというものではなく、キーボードから入力されたキー情報をデスクトップアプリ上に表示させようと思います。

動機は、キーロガーを実装することで、今後私が計画している便利アプリに組み込めるのではないかと思ったためです。

本アプリは WindowsFormsApp で作成しました。

過去の記事(WpfApp)

参考 1:ノートパソコンの画面の明るさをボタン一つで変更する【C#】
参考 2:ノートパソコンの画面の音量をボタン一つで変更する【C#】
参考 3:ノートパソコンの画面のバッテリー状況をボタン一つで取得する【C#】
参考 4:ノートパソコンの画面の通信料状況をボタン一つで取得する【C#】
参考 5:ノートパソコンで現在使用中のアプリをボタン一つで把握する【C#】
参考 6: ノートパソコンで現在使用中のオーディオ機器情報をボタン一つで取得する【C#】
参考 7:パソコンのユーザー情報取得やアプリ起動・終了、ディスプレイ画面の切替などを、ボタンを作って動かす【C#】

過去の記事(WindowsFormsApp)

参考 1:パソコンに接続しているディスプレイ情報をボタン一つで取得する【C#】

イメージ画像

UI はプロトタイプのため、特にこだわりはありません。

キーボードから入力された文字が表示されるだけの、シンプルな UI となっております。

今回のアプリでは、キーボードから入力された文字(アルファベット、数字、Ctrl キー、Shift キー、alt キーなど)が、本アプリ上に表示されます。

key_logger.gif

ソース

Form1.Designer.cs

Form1.Designer.cs
namespace WindowsFormsApp1
{
    partial class Form1
    {
        /// <summary>
        /// 必要なデザイナー変数です。
        /// </summary>
        private System.ComponentModel.IContainer components = null;

        /// <summary>
        /// 使用中のリソースをすべてクリーンアップします。
        /// </summary>
        /// <param name="disposing">マネージド リソースを破棄する場合は true を指定し、その他の場合は false を指定します。</param>
        protected override void Dispose(bool disposing)
        {
            if (disposing && (components != null))
            {
                components.Dispose();
            }
            base.Dispose(disposing);
        }

        #region Windows フォーム デザイナーで生成されたコード

        /// <summary>
        /// デザイナー サポートに必要なメソッドです。このメソッドの内容を
        /// コード エディターで変更しないでください。
        /// </summary>
        private void InitializeComponent()
        {
            this.labelKey = new System.Windows.Forms.Label();
            this.labelShift = new System.Windows.Forms.Label();
            this.labelCtrl = new System.Windows.Forms.Label();
            this.labelAlt = new System.Windows.Forms.Label();
            this.label1 = new System.Windows.Forms.Label();
            this.label2 = new System.Windows.Forms.Label();
            this.SuspendLayout();
            //
            // labelKey
            //
            this.labelKey.AutoSize = true;
            this.labelKey.Font = new System.Drawing.Font("MS UI Gothic", 48F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(128)));
            this.labelKey.Location = new System.Drawing.Point(426, 37);
            this.labelKey.Name = "labelKey";
            this.labelKey.Size = new System.Drawing.Size(226, 80);
            this.labelKey.TabIndex = 0;
            this.labelKey.Text = "label1";
            this.labelKey.Click += new System.EventHandler(this.labelKey_Click);
            //
            // labelShift
            //
            this.labelShift.AutoSize = true;
            this.labelShift.Font = new System.Drawing.Font("MS UI Gothic", 48F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(128)));
            this.labelShift.Location = new System.Drawing.Point(426, 172);
            this.labelShift.Name = "labelShift";
            this.labelShift.Size = new System.Drawing.Size(226, 80);
            this.labelShift.TabIndex = 1;
            this.labelShift.Text = "label2";
            this.labelShift.Click += new System.EventHandler(this.labelShift_Click);
            //
            // labelCtrl
            //
            this.labelCtrl.AutoSize = true;
            this.labelCtrl.Font = new System.Drawing.Font("MS UI Gothic", 48F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(128)));
            this.labelCtrl.Location = new System.Drawing.Point(426, 252);
            this.labelCtrl.Name = "labelCtrl";
            this.labelCtrl.Size = new System.Drawing.Size(226, 80);
            this.labelCtrl.TabIndex = 2;
            this.labelCtrl.Text = "label3";
            this.labelCtrl.Click += new System.EventHandler(this.labelCtrl_Click);
            //
            // labelAlt
            //
            this.labelAlt.AutoSize = true;
            this.labelAlt.Font = new System.Drawing.Font("MS UI Gothic", 48F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(128)));
            this.labelAlt.Location = new System.Drawing.Point(426, 332);
            this.labelAlt.Name = "labelAlt";
            this.labelAlt.Size = new System.Drawing.Size(226, 80);
            this.labelAlt.TabIndex = 3;
            this.labelAlt.Text = "label4";
            this.labelAlt.Click += new System.EventHandler(this.labelAlt_Click);
            //
            // label1
            //
            this.label1.AutoSize = true;
            this.label1.Font = new System.Drawing.Font("MS UI Gothic", 19.8F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(128)));
            this.label1.Location = new System.Drawing.Point(106, 69);
            this.label1.Name = "label1";
            this.label1.Size = new System.Drawing.Size(305, 34);
            this.label1.TabIndex = 4;
            this.label1.Text = "キーボード入力文字:";
            //
            // label2
            //
            this.label2.AutoSize = true;
            this.label2.Font = new System.Drawing.Font("MS UI Gothic", 19.8F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(128)));
            this.label2.Location = new System.Drawing.Point(74, 293);
            this.label2.Name = "label2";
            this.label2.Size = new System.Drawing.Size(337, 34);
            this.label2.TabIndex = 5;
            this.label2.Text = "Ctrl, Shift or Alt 判定:";
            //
            // Form1
            //
            this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F);
            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
            this.ClientSize = new System.Drawing.Size(800, 450);
            this.Controls.Add(this.label2);
            this.Controls.Add(this.label1);
            this.Controls.Add(this.labelAlt);
            this.Controls.Add(this.labelCtrl);
            this.Controls.Add(this.labelShift);
            this.Controls.Add(this.labelKey);
            this.Name = "Form1";
            this.Text = "Form1";
            this.Load += new System.EventHandler(this.Form1_Load);
            this.ResumeLayout(false);
            this.PerformLayout();

        }

        #endregion

        private System.Windows.Forms.Label labelKey;
        private System.Windows.Forms.Label labelShift;
        private System.Windows.Forms.Label labelCtrl;
        private System.Windows.Forms.Label labelAlt;
        private System.Windows.Forms.Label label1;
        private System.Windows.Forms.Label label2;
    }
}

Form1.cs

Form1.cs
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Drawing;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace WindowsFormsApp1
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();

            labelKey.Text = "";
            labelShift.Text = "";
            labelCtrl.Text = "";
            labelAlt.Text = "";
        }

        KeyboardHook keyboardHook = new KeyboardHook();

        protected override void OnLoad(EventArgs e)
        {
            keyboardHook.KeyDownEvent += KeyboardHook_KeyDownEvent;
            keyboardHook.KeyUpEvent += KeyboardHook_KeyUpEvent;
            keyboardHook.Hook();
        }

        // A-Zキーが押されているときは非0が入る
        int AtoZ = 0;

        private void KeyboardHook_KeyDownEvent(object sender, KeyEventArg e)
        {
            KeysConverter kc = new KeysConverter();

            if (e.KeyCode >= 65 && e.KeyCode <= 90)
            {
                AtoZ = e.KeyCode;
                labelKey.Text = kc.ConvertToString(e.KeyCode);
            }
            else if (e.KeyCode == 160 || e.KeyCode == 161)
                labelShift.Text = "Shift";
            else if (e.KeyCode == 162 || e.KeyCode == 163)
                labelCtrl.Text = "Ctrl";
            else if (e.KeyCode == 164 || e.KeyCode == 165)
                labelAlt.Text = "Alt";
            else
                labelKey.Text = kc.ConvertToString(e.KeyCode);
        }

        private void KeyboardHook_KeyUpEvent(object sender, KeyEventArg e)
        {
            KeysConverter kc = new KeysConverter();

            if (e.KeyCode >= 65 && e.KeyCode <= 90 && AtoZ == e.KeyCode)
            {
                AtoZ = 0;
                labelKey.Text = "";
            }
            else if (e.KeyCode == 160 || e.KeyCode == 161)
                labelShift.Text = "";
            else if (e.KeyCode == 162 || e.KeyCode == 163)
                labelCtrl.Text = "";
            else if (e.KeyCode == 164 || e.KeyCode == 165)
                labelAlt.Text = "";
            else if (AtoZ == 0)
                labelKey.Text = "";
            else
                labelKey.Text = kc.ConvertToString(AtoZ);
        }

        protected override void OnFormClosing(FormClosingEventArgs e)
        {
            keyboardHook.UnHook();
        }

        private void labelAlt_Click(object sender, EventArgs e)
        {

        }

        private void labelShift_Click(object sender, EventArgs e)
        {

        }

        private void labelKey_Click(object sender, EventArgs e)
        {

        }

        private void labelCtrl_Click(object sender, EventArgs e)
        {

        }

        private void Form1_Load(object sender, EventArgs e)
        {

        }
    }

    class KeyboardHook
    {
        protected const int WH_KEYBOARD_LL = 0x000D;
        protected const int WM_KEYDOWN = 0x0100;
        protected const int WM_KEYUP = 0x0101;
        protected const int WM_SYSKEYDOWN = 0x0104;
        protected const int WM_SYSKEYUP = 0x0105;

        [StructLayout(LayoutKind.Sequential)]
        public class KBDLLHOOKSTRUCT
        {
            public uint vkCode;
            public uint scanCode;
            public KBDLLHOOKSTRUCTFlags flags;
            public uint time;
            public UIntPtr dwExtraInfo;
        }

        [Flags]
        public enum KBDLLHOOKSTRUCTFlags : uint
        {
            KEYEVENTF_EXTENDEDKEY = 0x0001,
            KEYEVENTF_KEYUP = 0x0002,
            KEYEVENTF_SCANCODE = 0x0008,
            KEYEVENTF_UNICODE = 0x0004,
        }

        [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        private static extern IntPtr SetWindowsHookEx(int idHook, KeyboardProc 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("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        private static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam, IntPtr lParam);

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

        private delegate IntPtr KeyboardProc(int nCode, IntPtr wParam, IntPtr lParam);

        private KeyboardProc proc;
        private IntPtr hookId = IntPtr.Zero;

        public void Hook()
        {
            if (hookId == IntPtr.Zero)
            {
                proc = HookProcedure;
                using (var curProcess = Process.GetCurrentProcess())
                {
                    using (ProcessModule curModule = curProcess.MainModule)
                    {
                        hookId = SetWindowsHookEx(WH_KEYBOARD_LL, proc, GetModuleHandle(curModule.ModuleName), 0);
                    }
                }
            }
        }

        public void UnHook()
        {
            UnhookWindowsHookEx(hookId);
            hookId = IntPtr.Zero;
        }

        public IntPtr HookProcedure(int nCode, IntPtr wParam, IntPtr lParam)
        {
            if (nCode >= 0 && (wParam == (IntPtr)WM_KEYDOWN || wParam == (IntPtr)WM_SYSKEYDOWN))
            {
                var kb = (KBDLLHOOKSTRUCT)Marshal.PtrToStructure(lParam, typeof(KBDLLHOOKSTRUCT));
                var vkCode = (int)kb.vkCode;
                OnKeyDownEvent(vkCode);
            }
            else if (nCode >= 0 && (wParam == (IntPtr)WM_KEYUP || wParam == (IntPtr)WM_SYSKEYUP))
            {
                var kb = (KBDLLHOOKSTRUCT)Marshal.PtrToStructure(lParam, typeof(KBDLLHOOKSTRUCT));
                var vkCode = (int)kb.vkCode;
                OnKeyUpEvent(vkCode);
            }
            return CallNextHookEx(hookId, nCode, wParam, lParam);
        }

        public delegate void KeyEventHandler(object sender, KeyEventArg e);
        public event KeyEventHandler KeyDownEvent;
        public event KeyEventHandler KeyUpEvent;

        protected void OnKeyDownEvent(int keyCode)
        {
            KeyDownEvent?.Invoke(this, new KeyEventArg(keyCode));
        }
        protected void OnKeyUpEvent(int keyCode)
        {
            KeyUpEvent?.Invoke(this, new KeyEventArg(keyCode));
        }

    }

    public class KeyEventArg : EventArgs
    {
        public int KeyCode { get; }

        public KeyEventArg(int keyCode)
        {
            KeyCode = keyCode;
        }
    }
}

おわりに

今回はボタン一つで何かする、というものではなかったのですが、今後の便利アプリにうまい具合に組み込めていければいいなと思っています。

誰かのお役に立てれば幸いです。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【C#】簡易コンパイルプログラム

C#の自己学習用の小ネタメモです。
ちょっとVisual Studioを用いずにコンパイルしたいなという時に、
手間だったので簡易で簡素なプログラムを組んでみました。

◆ 環境
【OS】Windows10

C# コンパイル用バッチの作成

1.コマンドプロンプトを起動。
2.以下のコードをコマンドプロンプトに入力。
  デフォルトだとログインしているユーザーフォルダに
  生成されます。

【コマンドプロンプト】

 notepad csc.bat

3.作成されたcsc.batに以下のコードを記述。

csc.bat
C:\Windows\Microsoft.NET\Framework\v4.0.30319\csc.exe -nologo %1

4.保存して閉じる。

使い方

1.コマンドプロンプトを起動。
2.以下のコードを記述する。

【コマンドプロンプト:単体コンパイルの場合】

csc (絶対パス/相対パス)\コンパイルしたいファイル.cs 

 半角スペースをあけることで複数コンパイルすることも可能です。

【コマンドプロンプト:複数コンパイルの場合】

csc (絶対パス/相対パス)\コンパイルしたいファイル1.cs (絶対パス/相対パス)\コンパイルしたいファイル2.cs 

個人的に使いそうなオプション

▼『-nologo
 :コンパイラの開始時のサインオン バナーの表示と、
  コンパイル時の情報メッセージの表示を抑制します。
▼『-target
 :コンパイル時の生成オプション。以下のようなものがある。(要リファレンス参照
 - 『exeファイルの生成』-target:exe
 - 『dllファイルの生成』-target:library

あとがき

 オプションは今回「-nologo」だけにしましたが、
 必要に応じてリファレンスから引っ張って付け加えてください。
 また、環境変数に登録しておいて運用する形でも大変便利だと思います。
 そのうちまた加筆するかもしれません。

参考文献

C# コンパイラオプション【Microsoft Docs】

◆ 更新履歴
[20210315]:記事公開

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ジュークボックス

ジュークボックスを作成しました。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む