黒帯表示アプリ

早いものでもう三月ですね。
今回は題の通りで黒帯表示アプリを作った話となります。

どのようなアプリかと言いますと本当に単純で、黒いだけの画面を表示するためのアプリとなります。
デスクトップなど視界に入れたくない箇所の上に表示してそれら覆い隠すのが目的です。

経緯としては、モニタと自身の位置関係の都合で、
自身の正面に位置するモニタ中央~右側にVSなどのエディタを配置することが多いのですが、
そうすると余った左側のスペースからデスクトップや他ウィンドウが覗いており、
何となく落ち着かないと常々思っていました。
(デスクトップにはファイルを置きたい派なので、デスクトップを空にする選択肢はなしでした。
 またVSならアプリ内の配置が可変なのでフルスクリーンで何とかなるのですが、
 他のエディタなどはどうにもならずでして、、、)
課題を感じつつ何もしていなかったのですが、今更隠してしまえば良いことに気付いたため
WindowsFormにてさっくり実装しました。


namespace BlackBeltViewer;

public partial class Form1 : Form {

    // 黒帯モードか
    private bool IsBlackBeltMode { get; set; }

    public Form1() {
        InitializeComponent();

        // button1
        button1.Dock = DockStyle.Fill;
        button1.FlatStyle = FlatStyle.Flat;
        button1.Click += button1_Click;
        // Form1
        this.Text = "BlackBeltViewer";
        this.Load += Form1_Load;
    }
    
    private void Form1_Load(object sender, EventArgs e) {
        Draw();
    }

    private void Draw() {
        var color = IsBlackBeltMode ? SystemColors.Desktop : SystemColors.Control;
        this.FormBorderStyle = IsBlackBeltMode ? FormBorderStyle.None : FormBorderStyle.Sizable;
        this.BackColor = color;
        button1.BackColor = color;
        if (IsBlackBeltMode) {
            // FormBorderStyle.Noneにより枠が非表示となりフォームが縮むのでざっくり大きく
            this.Height += 30;
            this.Width += 8;
        }
    }

    private void button1_Click(object sender, EventArgs e) {
        // クリックでモード切り替え
        IsBlackBeltMode = !IsBlackBeltMode;
        Draw();
    }
}

コードを見てもらえばわかりますが本当に単純で、
・UIは画面に全面を覆ったボタン1つだけ
・ボタンのクリックで通常/黒帯モードを切り替え
・黒帯モード時に画面の枠を非表示・黒塗り・サイズ若干拡大
と言ったアプリとなります。
WindowsFormでプロジェクトを作成し、Form1にボタンを1つ配置し、
上記コードをForm1.csに貼るだけで動作するはずです。
(コンストラクタで行っているプロパティのセットやイベント登録は、
 実際にはデザイナーからプロパティウィンドウを開いて行ないました)

一瞬で作れるアプリですが、個人的にはかなりQoLならぬQoWが向上して満足しています。
以上です。

AWS学習中

AWSの案件に携わる可能性が出てきたため、12月ごろからちょくちょくAWSの学習をしています。
クラウド事情に疎く、クラウドサーバーとDBやストレージのイメージしか持っていなかったのですが、
様々なサービスが存在していて驚きました。
個人的にはサーバーレスでコードを実行できる AWS Lambda が気になっています。
インフラの管理が不要でアプリ開発にのみ集中できるとのことでとても便利そうです。
処理時間は最大15分とのことですが、分割可能な処理であれば複数のLamdaに分割し、
途中データはDBやストレージに保管するようにするなど工夫もできそうだなと思っています。

学習は主に AWS Skill Builder で行っています。
基礎コースは基本的に無料なのでとてもありがたいです。
座学形式のコースだけでなく、実際に手を動かしてみるコースもあり学習しやすいです。
aws自体のアカウントは不要で、一時的なアカウントを用意してもらえてそこで試すことができます。
しかし、概念の学習には十分に感じているのですが、実務で使用するには足りない気がしています。
各サービスの設定画面を見てみると設定可能な項目が多数存在し、
それらをどのように設定すべきかのベストプラクティスがわからないため、
他の何かでの学習したり自分で色々試してみる必要がありそうです。
AWSのアカウントは半年間、完全無料(クレジット上限と機能制限有)で
安心して使用できるようなので近々試してみようと思います。
(以前まではそうではなかったようですね。
 初めたてでも設定を誤ると請求が飛んで来たようで、、、)

何かあればまた書こうと思います。以上です。
(昨年末の記事にて少し触れたVS2026で拡張機能が動作しなくなった件は、
 大した問題でなかったので該当記事に追記しました)

MAUIプロジェクトのVS2026対応

先月Visual Studio2026が正式リリースされましたね。
弊社でも開発に使用すべく導入準備を進めております。
インストール時に2022の設定やコンポーネントなど引き継ぐ選択肢があったおかげで、
Windowsアプリのプロジェクトはすんなりと問題なく開発可能な状態にできました。
しかし、MAUIによるモバイルアプリプロジェクトの対応が難航したので、
ここに注意点などを記述しようかと思います。

1. プロジェクトの.NETバージョンは支障がなければ素直に10に上げるのが良いと思われます。
VS2026をインストールすると.NET8ではビルドできなくなります。(VS2022においてさえも!)
.NET9も試したのですが、こちらもiOSビルド時に発生するエラーを解決できず、時間を溶かしました。
他の方も.NET10に上げたら上手く行ったという報告を見かけたので、
ほぼ間違いなく.NET10が推奨だと思われます。

2. .NET10に上がったことで、iOSをビルドするXcodeのバージョンも上げる必要があります。
新しい環境でビルドが成功するまでは古いバージョンのXcodeを残しておきたいと思うのですが、
その場合はAppStoreで更新をせずにAppleのDevelopperサイトにてダウンロードしましょう。
xip形式でなので展開すればXcode.appファイルが出来上がります。
これをアプリケーションフォルダにドラッグすれば更新できるのですが、
そのままだとおそらく上書きされてしまうので、
一旦アプリケーションフォルダ内の古い方を Xcode_16.4.app などにしてドラッグしましょう。
これでXcodeのバージョンが異なるとエラーで怒られることはなくなるかと思います。
ただし、メジャーバージョンが同じもの(例えば26.0と26.2)を同居させた場合に、
新しい方をXcode.appとしていても古い方が検出されてしまいエラーが発生したので、
メジャーバージョンが異なる場合のみ同居可能なのかもしれません。
(一先ず古い方をゴミ箱送りにすれば動作しましたが、
 デフォルトパス設定をちゃんとすれば問題起きないのでしょうか、、、?)
また実機実行に必要なデバイスサポートファイルを新しいXcode内に格納する必要もあります。
 Xcode右クリック > パッケージの内容を表示 >
  ”Contents” > “Developer” > “Platforms” > “iPhoneOS.platform” > “DeviceSupport”
古いXcode内のDeviceSupportからコピーするのが楽かもしれません。

3. .NETのバージョンが切り替わった後や、global.jsonで.NETのバージョンを指定した後は、
プロジェクト直下でcmdを開き、以下の2コマンドを順に実行し、
sdkが意図したバージョンになることを確認しましょう。
dotnet workload restore → dotnet –version

4. .NETのバージョンを切り替えた後にiOSをビルドする場合は、
古いバージョンのビルド情報がMacに残っておりビルド失敗の原因になり得るので、
ローカルとMacそれぞれからXamarin関係のキャッシュを一度削除しましょう。
・ローカルでは以下の2フォルダがキャッシュなので削除
 %localappdata%\Temp\Xamarin\XMA
 %localappdata%\Xamarin\MonoTouch
・Macでは以下の2フォルダがキャッシュなので削除
 $HOME/Library/Caches/Xamarin/XMA
 $HOME/Library/Caches/maui/PairToMac
Macとのペアリングが成功しなくなった場合などもこれで解決することが多いです。

5. .NETのバージョンを上げた結果、ビルド時に以下のようなエラーが発生するようになりました。
(iOSシミュレータ実行時のもので、iOS実機時やAndroid時は対応する箇所の記述が異なります)
> 資産ファイル ‘D:\VSTS\Komatsu720623\Project\Mobile\KOM-MICS Mobile\obj\project.assets.json’ に ‘net10.0-ios/iossimulator-x64′ のターゲットがありません。
> 復元が実行されたこと、および ‘net10.0-ios’ がプロジェクトの TargetFrameworks に含まれていることを確認してください。プロジェクトの RuntimeIdentifiers に ‘iossimulator-x64′ を組み込む必要が生じる可能性もあります。
エラーメッセージの示唆に従ってRuntimeIdentifiersやRuntimeIdentifierにてターゲットを指定しても、
特定のターゲットのみ成功するようになることがあるだけで根本的に解決しません。
おそらくプロジェクトのキャッシュに前のターゲット情報が残っていることが原因なので、
以下のようにキャッシュを削除及びパッケージの依存関係の復元を行いましょう。
① Project直下のbinフォルダとobjフォルダを削除
② Project直下でcmdを開き、dotnet restore(※)
これにより、Androidならリビルド、iOSならMacとペアリング→リビルドにより
ビルド及び実行が成功するようになると思います。
※この dotnet restore は前述の dotnet workload restore よりも必ず後に実行してください。
 dotnet restore → dotnet workload restore の順だとビルド成功しません。

6. .NETのバージョン更新や、(MAUIは無関係ですが)Androidの要件変更により、
いくつかのパッケージの追加・更新を行う必要がありました。
今回はそれで解決したので良いのですが、
もしも保守されていないパッケージで問題が起こったら、、、と思うとぞっとします。
そう考えると依存するパッケージも極力メジャーで保守され続けるもののみにした方が良いですね。
モバイルアプリ開発はマルチプラットフォームの普及もあり一度リリースするまでは手軽ですが、
そこで終わりではなくリリースを保つためにOSやSDKのアップデートに追従して
保守し続けないといけないのが大変だと実感しました。。。

※12/25: 諸事情により記入途中です。後日追記します。
 →12/29: 追記完了しました。

何とかシミュレータ/実機共にデバッグ実行できるようになり、
またリリースビルドとストアへのアップロードも成功し一安心しました。

しかし、これで2026対応は終わりではありませんでした。
VSの社内拡張機能が動作しなくなったのです。
しかも2026だけでなく2022でも動作しません、、、
まだ調査中なのですが、次回はそれについて書こうと思います。(※↓)

それでは皆さん良いお年をお迎えください。

※ 26/2/6追記
VS2026にて一部破壊的な仕様変更が行われたようです。
社内拡張機能の処理にて、エディタを最新の状態にするために
削除→Undoにより編集イベントを発生させ最新を反映させる実装となっていたのですが、
このイベントがUndoスタックに乗らなくなり、Undoが機能しなくなっておりました。
代わりにカーソル移動により編集イベントの発生を試行するように修正し対応しました。

AutoHotKeyにより日報の入力を半自動化

今回は題の通りで、日報の入力作業をAutoHotKeyによって半自動化した話となります。

弊社の日報はWebで入力する形式です。
しかし、自分は前日の日報をコピーして編集・記入することが多いためWebで直接入力せず、
①テキストファイルで作成→②翌日は前日のファイルをコピー・編集し作成→③Webに転記
のように入力していたのですが、
この③のWebへの転記作業にて1項目ずつコピペする必要があり大変煩わしく思っていました。
※今年度からは日報に詳細は記載する必要はなくなったため簡素な入力でも問題ないのですが、
 翌日の作業の整理と翌週の朝礼で報告する内容の確認のために
 自分は以前と同じように入力しています。

煩わしく思いつつ特に何もしていなかったのですが、
転記作業自体は自動化すれば良いことに今更気が付きました。
そこでチャットAIにWebへの入力作業を自動化するのに適した手法を聞いてみたところ、
単純な作業なら作業自動化用スクリプト言語のAutoHotKeyが良さそうだと教えてもらい、
AutoHotKeyで転記処理を実装することにしました。

一旦チャットAIにコードを書いてもらったのですが上手く動作しなかった(※)ので、
それをひな型に自分で組み直しました。
※AutoHotKeyで何ができるのかわかっていなかったのもあり、そもそも指示が良くなかったです。
実装したコードは下記になります。

内容としては、
1. 起動時にクリップボードから入力データを読取
2. ホットキーで始動する処理内で、入力データをデータごと・項目ごとにキー送信
を実現しているコードとなります。
コードの前に参考までに、入力データ例(※)と入力画面を貼っておきます。
※入力データのフォーマットは今回の実装に合わせて少し調整しました。

組んでいる途中で思ったのですが、
単純な処理なのでC#でSendKeyを使用して実現するのでも良かった気がしますね。
ですがスクリプト言語は手軽ではあるので、一手段として触っておくのは意義がある気がします。
AutoHotKeyの文法自体は癖がなく書きやすいと感じました。
ただ代入が=ではなく:=なため、何度も癖でコロンを忘れてしまいエラーになりました。。。

以上、日報入力の半自動化(テキスト手動編集後のWebへの入力作業自動化)を行った話でした。
今後も自動化可能な作業に見つかった際は折を見て自動化するようにしたいと思います。

入力データ例

9999
0.5
[雑務]
1. 掃除・朝礼
---
12345678
7.5
[計測ソフト開発]
1. 仕様書作成
2. 実装中
---
_
_
[明日の予定]
1. 計測ソフト開発
2. サーバープログラムビルド送付
---
_
_
[出退]
08:55
18:15

入力画面

実装したコード:DailyReportAutomation.ahk

#Requires AutoHotkey v2.0
;開発使用バージョン:v2.0.19
;-------------------------------------------------------------------------------
;[グローバル変数]
; 標準出力へのグローバル参照:
;  ファイル名に*を指定してFileOpenすることで標準出力へ書き込み
;  実行時にmoreコマンドを指定して出力のリダイレクトを行う必要あり
;  (moreなしだとエラーになる)
;  次のように実行→ AutoHotkey64.exe DailyReportAutomation.ahk | more
global Stdout := FileOpen("*", "w")

;-------------------------------------------------------------------------------
;[起動時初期化処理:クリップボードを読み取り入力用レコードデータ作成]
if !ClipWait(1) {
    ; クリップボードが空ならエラー終了
    MsgBox("No text found in clipboard.", "Error", 16)
    ExitApp
}
buff := ClipboardAll() ; クリップボードを退避
clipboard := A_Clipboard ; クリップボードを取得
records := Parse(clipboard) ; クリップボードからレコードリストをパース
for item in records
    Log(item.ToString() . "`n")
A_Clipboard := buff ; 退避していたクリップボードを復元
if (records.Length = 0) {
    ; クリップボードが空ならエラー終了
    MsgBox("No records in clipboard.", "Error", 16)
    ExitApp
}
if (records.Length > 10) {
    ; クリップボードが10以上ならエラー終了
    MsgBox("Too much records in clipboard.", "Error", 16)
    ExitApp
}
; エラーなければ常駐開始

;-------------------------------------------------------------------------------
;[常駐時ホットキー]
;1. Windows + s:入力処理開始(開始前に先頭の入力欄にカーソルを合わせておくこと
#s:: {
    ; IMEが全角か半角かチェックし、全角(true)なら半角に切り替えます
    if (CheckImeIsFullOrHalf("Infortec 日報"))
        SwithImeToFullorHalf()
    ; 与えられたレコードデータの項目を順に入力
    for record in records {
        ; 工番入力→案件名入力→内容入力→コメント欄飛ばす→進捗欄飛ばす→時間入力
        ClearSend(record.ProjectNo)
        Next()
        ClearSend(record.ProjectName)
        Next()
        ClearSend(record.Content)
        Next()
        Next()
        Next()
        ClearSend(record.Time)
        Next()
    }
    ExitApp ; アプリ終了
}

;2. Windows + q:アプリ終了
#q:: {
    ExitApp
}

;-------------------------------------------------------------------------------
;[クラス]
;レコードクラス
class RecordData{
    ;コンストラクタ:工番、時間、案件名、内容
    __New(projectNo, time, projectName, content){
        this.ProjectNo := projectNo
        this.Time := time
        this.ProjectName := projectName
        this.Content := content
    }

    ;Func 文字列化
    ToString(){
        txts := [this.ProjectNo, this.Time, this.ProjectName, this.Content]
        return Join(txts, "`n")
    }
}

;-------------------------------------------------------------------------------
;[関数群]
;Func 指定したタイトルを持つウィンドウのIMEが全角か半角かチェックします
CheckImeIsFullOrHalf(windowTitle) {
    ; 日報ウィンドウのIMEの状態を取得
    SetTitleMatchMode 1 ; タイトル部分一致
    WM_IME_CONTROL := 0x0283
    IMC_GETOPENSTATUS := 0x0005
    imeWnd := DllCall("imm32.dll\ImmGetDefaultIMEWnd", "Uint", WinExist(windowTitle))
    imeStatus := DllCall("user32.dll\SendMessageA", "UInt", imeWnd, "UInt", WM_IME_CONTROL, "Int", IMC_GETOPENSTATUS, "Int", 0)
    Log(imeStatus ? "全角" : "半角")
    return imeStatus
}

;Func IMEの全角/半角を切り替えます。
SwithImeToFullorHalf() {
    Send("{vkF3sc029}") ; 半角/全角キー送信
}

;Func 入力欄をクリアして与えられた文字列を入力します
ClearSend(text) {
    Sleep 80
    Send("^a")
    Sleep 20
    Send("{Del}")
    Sleep 20
    SendText(text)
    Sleep 80
}

;Func 次の入力欄へ移動します
Next(){
    Send("{Tab}")
    Sleep 20
}

;Func テキストからレコードリストをパースします
Parse(text){
    ; 改行を正規化
    text := StrReplace(text, "`r`n", "`n")
    text := StrReplace(text, "`r", "`n")
    ; 項目ごとに文字列を分割
    items := StrSplit(text, "---")
    ; 項目が複数ないなら終了
    if (items.Length < 2)
        return []
    records := []
    for item in items {
        ; 項目からレコードを読み取る
        item := Trim(item, "`n")
        ; 行ごとに分割
        rows := StrSplit(item, "`n")
        ; 4行未満なら無効データとして飛ばす
        if (rows.Length < 4)
            Continue
        ; 行ごとの処理:主に複数行に跨る内容行の結合を行う
        contents := []
        for index, row in rows {
            ; プレースホルダ _ は空行に変換
            rows[index] := row := row = "_" ? "" : row
            ; 4項目目までは飛ばす
            if (index < 4){
                Continue
            }
            ; 4項目目以降を内容行配列に追加
            contents.Push(row)
        }
        content := Join(contents, "`n")
        ; レコード作成:工番1行目、時間2行目、案件名3行目、内容4行目~
        record := RecordData(rows[1], rows[2], rows[3], content)
        records.Push(record)
    }
    return records
}

;Func 文字列配列を区切り文字で連結
Join(array, delimiter){
    ret := ""
    ; 戻り値に配列内の各文字列を区切り文字と合わせて結合
    for str in array
	    ret .= str . delimiter
    ret := RTrim(ret, delimiter) ; 末尾の区切り文字を削除
    return ret
}

;Func ログ出力
Log(msg){
    Stdout.WriteLine(msg)
}

ログイン処理改造に伴うDB情報取得方法の仕様見直し

開発しているサービスのセキュリティ向上のため、
ログイン時に連続して失敗した場合に一定時間のログイン制限処理を実装します。
ログイン時のエラー情報は既にDBに記録されていたので、
単純にログイン開始時及びエラー時にDBを検索しようと思ったのですが、
データ数次第で検索に時間がかかる・DBと接続できない場合に取得不可など、
ログイン処理の長時間化や動作不良の懸念がありました。
そのため、ログイン時にはDB検索を行わずに済むよう、
エラー記録時にサーバーにてキャッシュし、
ログイン処理にてエラー情報取得時にキャッシュから取得する方針にしました。
またこれだけだとサーバーが再起動した際にはキャッシュが消えてしまうので、
初期化時にDBを検索し、有効な情報のみキャッシュに追加することにします。
この方針で仕様を練り直して、問題なく動作するように実装したいと思います。

WordPressにて記事を投稿できなくなる問題を解決

先月からブログを投稿できなくなっておりました。
手が空いたので本日調査したところ、無事解決できたので、
以下にまとめます。

#症状
WordPressの投稿画面にて、寄稿者権限でないにも関わらず、「公開」ボタンが非表示となり、
代わりに「レビュー待ちとして送信」ボタンが表示されていました。
「レビュー待ちとして送信」ボタンをクリックするとエラーになり、
投稿はおろかレビュー待ちとして送信もできず。

#解決策
プラグイン Incorrect Datetime Bug Fix (※) をインストールして有効化しました。
※データの日付が ’0000-00-00 00:00:00′ の場合エラーとする設定を無効化するプラグイン
 Incorrect Datetime Bug Fix – WordPress plugin | WordPress.org

#経緯
レンタルサーバーの更新が先月頭にあったのでそれによるものではないかと言う話になり、
レンタルサーバー運営に問い合わせ。

WordPressが古過ぎるのが原因ではないかと言われる。

WordPressの更新は手間なので一旦保留に。

他の解決策を探す。
一番初めに見つかるのはDBの容量不足だが、容量は問題なし。

解決策は見つからないものの、wp-config.phpにて
define('WP_DEBUG', true);
を設定するとデバッグモードとなりエラーログなど出力できると判明。
WordPressで新規投稿ができなくなった原因がプラグインだった | #interest_ae

レンタルサーバーコントロールパネルから wp-config.php を開き、デバッグモードをオンに。

投稿画面を開くと以下のエラーログが表示。
 WordPress database error: [Incorrect datetime value: '0000-00-00 00:00:00' for column 'post_date_gmt' at row 1]
 INSERT INTO …
 …
 VALUES (…,’0000-00-00 00:00:00′,…)

エラー内容で検索して解決策記載ページを発見!
MySQLで設定変えたら、WordPressで投稿時に「Incorrect datetime value」エラーが出たので対処法 – たぬきのぶろぐ

#推測される原因
推測なのですが、おそらくレンタルサーバーの更新によりMySqlのバージョンが更新され、
その際に日付が無効値 ‘0000-00-00’ の場合に
エラーとなる設定が追加されたのではないかと思われます。
しかしWordPressの投稿開始時にDBに仮挿入される初期データ内の日付が、
ログを見るに ‘0000-00-00’ のため、投稿開始時にエラーとなり投稿できなくなってしまったのかと。
その問題となる設定を Incorrect Datetime Bug Fix プラグインは無効化してくれるので、
エラーにならずに投稿開始できるようになったのでしょう。

調べてみたらすんなり解決して良かったです。
毎度のことながら、Webの先人達に感謝!

Macの容量削減

VS for Macがサポート終了してからはMacで開発を行うことがなくなったため、
Macの用途はもっぱら次の3つです。
・MAUIのビルド(のためにWindowsのVSとペアリング)
・iPhoneアプリのリリース申請
・iPhoneアプリの証明書更新
今回MAUI9.0(.NET9.0)に対応する必要が生じ、
そのビルドのためにXcodeのバージョンアップを行おうと思ったのですが、
OSが対応していなかったためまずはOSの更新から行うことになりました。

しかし、Macの容量はもうパンパンで残り2GB(そんなことあります?)となっていたので、
Finder上で目に付いた不要そうなファイルを整理しました。
それにより空きが8GB弱となり、ダウンロードされるOSの容量7GB弱分を確保できたので
意気揚々と更新を開始したのですが、容量が足りないと怒られました。
更新時に25GB弱必要だそうです。Oh…

設定のストレージから整理しろ(※)と言われたのでストレージを見てみたところ、
詳細を確認可能な項目の内、デベロッパの項目が47GBと抜きんでていました。
※Macにいまだに不慣れなのでiOSと同じ形式でストレージが管理できるの失念していました
中身を見てみるとXcode関係で、
iOSデバイスサポートのセクションに数GB単位の項目がずらりと並んでいました。
消せそうだと思い調べてみたところ、

ここは、実機デバッグをするとシンボル情報などが実機からダウンロードされて保存されるようです。
実機のOSバージョン毎に追加され、一つが大きいので、肥大化しやすいようです。
消しても次回接続したときにまた作成されます。

引用元:開発macの空き容量を20GB以上増やした。 #iOS – Qiita

とのことだったので全て削除して50GB以上の空きができ、
無事OSの更新を開始できました。
長年デバッグのために接続したデバイス・OSの情報が蓄積され続けていたのですね。。。

以下のキャプチャは消している最中のものです。

大分容量に余裕が出たので、
MAUIのビルドのためにVSとMacをペアリングする際によく容量が少なくなってると注意を受けて
Xamarinのキャッシュを削除していたのですが、その必要もなくなりそうで良かったです。

以上です。

C#11と12の機能いくつか

本日から弊社のメインとなる案件のプロジェクトにて
使用可能なC#のバージョンが12まで引き上げられました。
(今までは9や10でした。)
使用可能になった11と12の機能にさらっと目を通して来たので、
知らなかった便利そうな機能や使ってみたかった機能を4つほど紹介してみます。

1. 生文字列リテラル

string longTxt = """
    toooooooooooooooooooo...
        looooooooooooooooooong...
            teeeeeeeeeeeeeeeeeeeeeext...
    """;

文字列をダブルクォーテーション3つで囲うことで、
エスケープなしで改行や特殊記号などを含めた生の文字列を
ソース内に直接記述可能となるそうです。
改行を含んだ長文の文字列の可読性が改善されてかなり便利そうなのですが、
社内ツールの英語化用日本語抽出ツールに対応が必要になりそうなのは懸念点です。

C# 11 の新機能 – C# ガイド – C# | Microsoft Learn

2. コレクション式

List<int> No = [1, 2, 3];

リストなどのコレクションの値をpythonのような構文で
簡潔に作成できるようになったようです。
毎度冗長だな、、、と思いながら書いていたのでありがたいです。

C# 12 の新機能 – C# ガイド – C# | Microsoft Learn

3. ファイル修飾子

file class LocalObject { }

可視性を記述されたソースファイル内に限定する修飾子です。
ファイル外のクラスとは名前の競合も起こらないので、
そのソースでしか使用しないちょっとしたクラスを定義するのに便利そうです。
使ってみたかったのでうれしいです。

ファイル キーワード – C# reference | Microsoft Learn

4. usingによるタプルのエイリアス

using AtoF = (int A, int B, int C, int D, int E, int F);

usingによりタプルに名前を付けることができます。
何度か登場して冗長に感じるもクラスにするほどではないタプルを
省略して書けるので便利そうです。
使いたいことが今まで何度かあって、
その度に言語バージョンに泣かされていたのでこれもうれしいです。

タプル型 – C# reference | Microsoft Learn

以上です。
他の機能も含め新機能使っていきたいです。

スクレイピング機能のエラー対応

先日、スクレイピング関係の機能の不具合対応を行いました。
スクレイピングにより抽出したWebページをWebViewにより表示する機能なのですが、
実行により404のエラーページが抽出され、エラー表示となってしまう不具合でした。
根本原因は対象のWebページの仕様変更による誤ったページの抽出だったので、
現在の仕様に合うように抽出処理の修正を行いました。

しかしこの修正を行った後でも、
正しく抽出したページが稀に404などのエラーページになる可能性があり、
その場合同じエラー表示となってしまうため、対策を入れることにしました。
WebページをWebViewで事前に開く前にHttpWebRequestによりHttpStatusCodeをチェックし、
エラーの400番台以上なら別のページを試行するようにしました。

これにて解決かと思ったのですが、人間なら問題なくアクセス可能なページでも
プログラムからだとbot対策のWebApplicationFirewall(reCAPTCHAなど)に引っ掛かり
エラーコードが返って来るケースもあるとわかりました。
そのケースのレスポンス内のHTTPについて調査したのですが、
サービスにより内容が異なるようで汎用的にWAFのレスポンスか判定は不可能に思われたので、
ひとまずWAFの可能性のあるエラーコード(※)の場合は通すようにしました。
これで一旦様子見してみます。
※403, 406, 418, 503, 1020(チャットAIにも教えてもらいました)

膨らんだ小型バッテリーの処分

先月自室の掃除をしていたところ、机の奥から1つ前のiPhoneが発掘されたのですが、
何と画面が筐体から離れてぱかっと半開きの状態になっていました。
(画面ってこんな風に開くものなのだと驚きました)
原因はお察しの通り、バッテリーの膨張によるものでした。
Amazonで工具を買い自分で取り外してみたところ、
バッテリーがぶよぶよになって破れそうに思える状態になっており、
気付かず放置していたらと思うと怖いです。(掃除して良かった、、、!)

結局その膨らんだバッテリーは近所のゴミ処理場に持って行って回収してもらいました。
自分の住んでいる市の場合、膨らんでおらずリサイクルマーク(※)の付いているものなら、
市が役所やスーパーに設置している回収ボックスに入れるだけで良いみたいなのですが、
膨らんでいるものは直接持ち込む必要があり少々面倒でした。
(近場の電気屋にも電話してみたのですが断られました。。。)

自治体によっては、膨らんでいたら問答無用で回収不可のところもあるようなので、
回収してもらえただけありがたいです。

皆様も家に眠っているバッテリーがあればお気を付けください。。。


今回もう使わないモバイルバッテリーもついでに捨てようと調べた結果初めて知ったのですが、
リサイクルマークの有無も回収の際にとても重要となるようです。
ところがどうも、Amazonで売られている無名の海外メーカーのモバイルバッテリーの中には、
肝心のリサイクルバッテリーのないものが存在するようで、
そのハズレを捨てようと思うとどこにも回収してもらえずたらい回し状態になるとか。
怖いのでバッテリー内臓の製品は日本製ないし有名メーカーの物だけ買うようにします。。。