普段から癖

こんにちはmtjです。

世の中にはマイコン、PC上で動いているソフトであったり様々な物がプログラムで動いています
UI等であれば飲食店だったり電子注文システム、ゲームであったり
センサー等の実際の動きであれば自動配膳、アミューズメント機器であったり

色々動きを見ていると面白い物があります。

自分は昔から気になった機械があれば 分解して中身を確認するような人間でした。
ジャンクのプレイステーション等ゲーム機器であったり ノートPCであったり様々な物を分解してきました。

観察している物がどのようなプログラム、センサー、機械で動いているかを考えるのは楽しいですし勉強にもなります。
現在の知識で動き方が分からないなら自分で情報を集めて調べて こう動いてるかもしれないと考えるのも楽しいですね。

そういった動きに興味があるからこそ今この業界にいるのだと思います。

初めてのCとC++

これまでの業務ではC#を使ってきました。
今回初めてCとC++に携わる機会があり、学習しました。
せっかくなのでModern C, C++ と言われる比較的新しいバージョンです。

C#を使い慣れているとCやC++ではロジックのコード量が増え、
こんなこともできないのか、と思う一方、
特にメモリの扱われ方などはより理解が深まりました。

ただC++は言語仕様としてもかなり複雑で、Cの次に学ぶにはとても難しい印象です。
なのでC++習得するには先にC#等を理解して、オブジェクト指向の特徴や、
最近の言語の機能、プログラミングスタイルなどを知った後のほうが、
C++を使ってどのような設計や関数を目指すべきかということがよく分かると思います。

RustもC++とよく比較されて人気があるようなので、
今後触れてみたいと思います。

WindowsFormsのDataGridViewにセルの情報を参照する右クリックメニュー設定

WindowsFormsのDataGridViewに右クリックメニュー(ContextMenuStrip)を設定し、
クリックされたセルの情報を参照しメニュー制限・機能実行できるようにしました。

最初はセルマウスクリックのイベントを使って、
クリックされたセルのインデックスを保管し、メニューをShowで出せばいいかと思ったのですが、
メニュー表示する座標を上手く計算できませんでした。

そこでどうすべきか調べたところ、
メニューの表示は、素直にContextMenuStripプロパティにメニュー設定してコントロールに任せて、
セルの情報をもとにメニュー制限するなどは、
ContextMenuStripのOpeningイベントで行うのが良いとわかりました。

結果として、以下のようなコードでの実現となりました。

private void menu_Opening(object sender, CancelEventArgs e) {
	// 画面座標
	var posScreen = Cursor.Position;
	// クライアント座標
	var posClient = dgvGroupingPartsNumData.PointToClient(posScreen);
	// クライアント座標をセル情報に変換
	var info = dgvGroupingPartsNumData.HitTest(posClient.X, posClient.Y);

	// info.RowIndex, info.ColumnIndexがクリックされたセルインデックス
	// それらをもとにメニューを一部制限
	// メニュー内機能からセルを参照するためにプロパティなどに保管
}

以上です。

DataGridViewの列幅オートサイズモードによって描画速度が遅い場合の対策

DataGridViewの列幅オートサイズモード(AutoSizeColumnsMode)がAllCellsなどのようにセルの内容によってオートサイズするモードの場合、セルのValueの設定回数が多いと描画が遅くなります。

これを改善するために、列幅オートサイズモードを一旦Noneに変更する拡張メソッドを作成しました。
これにより劇的に速度改善しました。

/// <summary>処理実行中に列幅のオートサイズを停止します。セルのValueの設定回数が多い場合、オートサイズ処理が遅いため、一旦オートサイズを停止することで高速化を行います。</summary>
public static void SuspendColumnAutoSize(this DataGridView dgv, System.Action action) {
    //注意:AutoSizeRowsModeは対象外。Noneに変更しただけで遅いため
 
    //finallyで行う処理
    System.Action finallyAction = () => { };
    //AutoSizeColumnsModeがNoneなどでない場合はNoneに変更する
    if (dgv.AutoSizeColumnsMode.EqualsAny(DataGridViewAutoSizeColumnsMode.None, DataGridViewAutoSizeColumnsMode.Fill).Not()) {
        var mode = dgv.AutoSizeColumnsMode;
        finallyAction += () => dgv.AutoSizeColumnsMode = mode;
        dgv.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.None;
    }
    //列のAutoSizeColumnsModeがNoneなどでない場合はNoneに変更する
    foreach (var col in dgv.Columns.ToList()) {
        if (col.AutoSizeMode.EqualsAny(DataGridViewAutoSizeColumnMode.None, DataGridViewAutoSizeColumnMode.NotSet, DataGridViewAutoSizeColumnMode.Fill).Not()) {
            var mode = col.AutoSizeMode;
            finallyAction += () => col.AutoSizeMode = mode;
            col.AutoSizeMode = DataGridViewAutoSizeColumnMode.None;
        }
    }
    //処理実行
    try {
        action();
    } finally {
        finallyAction();
    }
}

地方住みの苦悩

こんにちは mtj です。

自分は割りと地方の方に住んでおりますが 京都の繁華街も大阪の繁華街も絶妙に1時間ぐらいかかってしまうところに住んでます。
そのせいで何か買い物に行きたい、遊びに行きたいとなってもなかなか及び腰になってしまいます。

住むには静かで問題も少ないいいとこなのですが そういった点だけが悩みです。

往復2時間以上なので何か実物見て買いたい! となっても小旅行の気分です。
移動時間は本等を読むのでそれも味といえば味なんですが

C#でのウィンドウへのキー送信

結論としては、以下の2種類の方法があるとわかりました。

① .NetのSendKeys.SendWaitメソッド:
 → 最前面のウィンドウ限定。引数が単純で使いやすい。
② ・WindowsAPIのPostMessageメソッド:
 → 送信先のウィンドウ指定可能。引数が複雑で使いにくい。必要な引数はSpy++にて調査可能。

以下経緯などです。

今携わっている案件で、プログラムからキー送信を行うことで
外部のアプリをショートカットキーにより自動操作することになりました。

お客様の調査により、
.NetのSendKeys.SendWaitメソッドにより実現可能だとわかっていたのですが、
このメソッドは送信先を指定できず、必ず最前面のウィンドウにキー送信を行うもので、
自動操作中にユーザ操作などで最前面のウィンドウが切り替わってしまうと
誤ったアプリにキーを送信してしまうことになります。

そこで可能ならウィンドウを指定して送信するという方針になり、
社内メンバーからウィンドウのハンドルを引数に与えてキー送信できるメソッドがあると伺いました。

調べてみたところWindowsAPIのPostMessageがそれにあたるとわかり、
試してみたのですが、文字列を引数に与えるだけのSendKeys.SendWaitとは異なり、
引数が複雑(ハンドルとキー内容だけでなく、複数のパラメータを32bitに詰め込んだ値が必要)で、
目的のキーを送信するためにどうすればよいかわかりませんでした。

そこで更に調べてみたところ、VisualStudioに含まれるSpy++というアプリにより、
目的のソフトへキーを送信した際のメッセージをログ出力して、
送信時の引数を調べることができるとわかりました。
参考:
VBAのSendKeys,System.Windows.FormsのSendWaitなどが反応しないときに読む記事 – 適材適所

これにより、無事PostMessageによるキー送信をテストすることに成功しました。

結果的には、
操作対象のアプリはそもそも最前面に表示されている状態でないとキー送信による操作を受け付けないとわかったため、
今回はキー送信前に毎回ウィンドウのActivateする方針に決まりましたが、
PostMessage(+ Spy++による送信内容の調査)は今後何かしらに役に立ちそうだなと感じています。

また余談ですが、
調査中にPostMessageによりゲームにキー送信を行い自動で操作する旨の話をチラッと見かけました。
学生時代に見かけたゲームAIの研究で、
「ゲームの状態を画像処理で読み取り、行動を決定し、外部からゲームを操作するAI」
を見かけたことがあったのですが、
あの実現にはこの辺りのメソッドを呼んでいたのだなと懐かしみながら今更のように思いました。

以上です。

UAC(ユーザアカウント制御)などの有効/無効を取得

UAC(ユーザアカウント制御)や、「UACによる管理者昇格時の同意プロンプト表示」の有効/無効によって処理を変えたい場合があったので取得方法を調べました。

結果、レジストリを調べれば良いことが分かりました。簡単ですね。

以下のプロパティを実装してみました。

/// <summary>UAC(ユーザアカウント制御)が有効か</summary>
public static bool? IsUacEnabled {
    get {
        try {
            var key = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System");
            if (key == null) return null;
            var value = key.GetValue("EnableLUA");
            if (value == null) return null;
            return value.Equals(1);
        } catch {
            return null;
        }
    }
}
 
/// <summary>UACによる管理者昇格時の同意プロンプト表示が有効か</summary>
public static bool? IsAdminConsentPromptEnabled {
    get {
        try {
            var key = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System");
            if (key == null) return null;
            var value = key.GetValue("ConsentPromptBehaviorAdmin");
            if (value == null) return null;
            return value.Equals(0).Not();
        } catch {
            return null;
        }
    }
}

宇宙

こんにちは mtj です。

最近とある宇宙のゲームをやっているので やっぱり宇宙は面白いなと改めて感じました

スケールがなんでも大きい 光年なんて単位はなんて宇宙でないとほぼ聞かないと思います。
またワープとかの理論も面白いです。

Alcubierre driveというものがありますが 物体は光を超えることができないから 光の速度を超えているビックバンを起こして移動するという理論です。
そういった関連の話は見ていて面白いので読み物として読んでも面白いです。

また点在する星についても色々あって面白いですね ガス惑星だったり 氷で覆われていたり地球では起こり得ないようなことが起こっている星がいっぱいあったり
色々夢がありますね。

C#で配列やリストをforとforeachで走査する際の速度比較

先日、コードの速度チューニングを行ったのですが、
その際にC#ではforeach文よりもfor文の方が速いという話を思い出して、
書き換えを行う前に調査してみました。

次の記事を参考に速度調査させていただきました。ありがとうございます。
forとforeachのアクセス速度比較 – Qiita

個人的な結論としては、以下のようになりました。
・リスト使用時はfor文が速い(要素へのアクセスには注意)
・配列使用時はforeach文が速い
・リストよりも配列の方が速い
なので、カリカリにチューニングしたい場合は配列+foreachが良いのかもしれません。
(そこまでする必要がある場合は、実際のコードで速度比較して決定した方が良さそうです)

それぞれの速度比較は以下です。(処理時間は末尾に記載)

・リスト使用時の速度
for文(一時キャッシュあり)> ForEachメソッド > foreach文 >>> for文(一時キャッシュなし)
・配列使用時の速度
foreach文 > for文(一時キャッシュあり)> for文(一時キャッシュなし)

調査してわかったこと・わからなかったことは以下です。
・Listの値へのインデクサでのアクセスはその都度個数チェックが発生するので、
 何度も使う場合遅い。一時変数にキャッシュすべし。
・ListのEnumeratorでは個数以外のチェックも行っているので、
 個数チェックだけのfor文やArrayのEnumeratorより遅い
・配列の場合に、for文よりもforeachの方が速い理由は不明
 (個数へのアクセスが内部フィールドな分速いとか?)

処理速度を調査するにあたり
参考にさせていただいだ記事中のコードに以下のようなコードを追記し、
リストと配列の2パターン用意してテストしました。

for (var i = 0; i < testlist.Count; i++) {
	var value = testlist[i]; // 一時変数にキャッシュ
	for (var cnt = 0; cnt < 1000; cnt++) {
		buf = value;
	}
}

それぞれの処理時間は以下です。10回平均です。
(tmp = 一時キャッシュあり)

By List
for Loop : 204 ms
for Loop tmp : 79.5 ms
foreach Loop : 82.7 ms
ForEach Loop : 81.8 ms

By Array
for Loop : 67.8 ms
for Loop tmp : 50.7 ms
foreach Loop : 49.7 ms

以上です。

DisposeActionクラス

C#で後処理を行いたい場合にfinallyブロックに記載することが良くあります。
途中でreturnで抜けても、例外発生しても必ずfinallyブロックが実行されるので安心です。

たとえば以下のように記載します。


try {
    //ここで処理Aを実行
    counter++;
    //ここで処理Bを実行
} finally {
    counter--;
}

処理Aのあとにcounterを+1し、処理Bを実行後にcounterを-1するコードです。
これだと処理Aでreturnや例外発生するとcounterが-1されて正しく動作しないです。
このコードだと、処理Aだけtryブロックの外側で実行するだけで良いですが、そうもいかない場合も多々あったり。

というわけで、DisposeActionクラスを作ってみました。
いたってシンプルなクラスで、コンストラクタで与えたラムダ式をDisposeメソッド内で実行するだけです。
以下のように使用します。


//ここで処理Aを実行
counter++;
using var _ = new DisposeAction(() => counter--);
//ここで処理Bを実行

先程の問題が解決し、行数が減り、コードも見やすくなったのではなかろうかと思います。

DisposeActionのインスタンスは不要なので、本当は以下のように書けると良いのですが。現状のC#では出来ないです。


using new DisposeAction(() => counter--);